/*
 * Copyright (c) 2011-2025 PiChen
 */

package com.interface21.transaction;

import junit.framework.TestCase;

import com.interface21.transaction.support.AbstractPlatformTransactionManager;
import com.interface21.transaction.support.DefaultTransactionDefinition;
import com.interface21.transaction.support.TransactionCallbackWithoutResult;
import com.interface21.transaction.support.TransactionTemplate;

/**
 * @author Juergen Hoeller
 * @since 29.04.2003
 */
public class TransactionTestSuite extends TestCase {

    public void testNoExistingTransaction() {
        PlatformTransactionManager tm = new TestTransactionManager(false, true);
        TransactionStatus status1 = tm.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_SUPPORTS));
        assertTrue("Must not have transaction", status1.getTransaction() == null);

        TransactionStatus status2 = tm.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRED));
        assertTrue("Must have transaction", status2.getTransaction() != null);
        assertTrue("Must be new transaction", status2.isNewTransaction());

        try {
            TransactionStatus status3 = tm.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_MANDATORY));
            fail("Should not have thrown NoTransactionException");
        } catch (NoTransactionException ex) {
            // expected
        }
    }

    public void testExistingTransaction() {
        PlatformTransactionManager tm = new TestTransactionManager(true, true);
        TransactionStatus status1 = tm.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_SUPPORTS));
        assertTrue("Must have transaction", status1.getTransaction() != null);
        assertTrue("Must not be new transaction", !status1.isNewTransaction());

        TransactionStatus status2 = tm.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRED));
        assertTrue("Must have transaction", status2.getTransaction() != null);
        assertTrue("Must not be new transaction", !status2.isNewTransaction());

        try {
            TransactionStatus status3 = tm.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_MANDATORY));
            assertTrue("Must have transaction", status3.getTransaction() != null);
            assertTrue("Must not be new transaction", !status3.isNewTransaction());
        } catch (NoTransactionException ex) {
            fail("Should not have thrown NoTransactionException");
        }
    }

    public void testNonTransactionalExecution() {
        AbstractPlatformTransactionManager tm = new TestTransactionManager(false, false);
        tm.setAllowNonTransactionalExecution(true);
        assertTrue("Correctly allowed non-transaction execution", tm.getAllowNonTransactionalExecution());
        try {
            TransactionStatus status = tm.getTransaction(null);
            assertTrue("Must not have transaction", status.getTransaction() == null);
            tm.rollback(status);
        } catch (NoTransactionException ex) {
            fail("Should not have thrown NoTransactionException");
        }
    }

    public void testCommitWithoutExistingTransaction() {
        TestTransactionManager tm = new TestTransactionManager(false, true);
        TransactionStatus status = tm.getTransaction(null);
        tm.commit(status);
        assertTrue("triggered begin", tm.begin);
        assertTrue("triggered commit", tm.commit);
        assertTrue("no rollback", !tm.rollback);
        assertTrue("no rollbackOnly", !tm.rollbackOnly);
    }

    public void testRollbackWithoutExistingTransaction() {
        TestTransactionManager tm = new TestTransactionManager(false, true);
        TransactionStatus status = tm.getTransaction(null);
        tm.rollback(status);
        assertTrue("triggered begin", tm.begin);
        assertTrue("no commit", !tm.commit);
        assertTrue("triggered rollback", tm.rollback);
        assertTrue("no rollbackOnly", !tm.rollbackOnly);
    }

    public void testRollbackOnlyWithoutExistingTransaction() {
        TestTransactionManager tm = new TestTransactionManager(false, true);
        TransactionStatus status = tm.getTransaction(null);
        status.setRollbackOnly();
        tm.commit(status);
        assertTrue("triggered begin", tm.begin);
        assertTrue("no commit", !tm.commit);
        assertTrue("triggered rollback", tm.rollback);
        assertTrue("no rollbackOnly", !tm.rollbackOnly);
    }

    public void testCommitWithExistingTransaction() {
        TestTransactionManager tm = new TestTransactionManager(true, true);
        TransactionStatus status = tm.getTransaction(null);
        tm.commit(status);
        assertTrue("no begin", !tm.begin);
        assertTrue("no commit", !tm.commit);
        assertTrue("no rollback", !tm.rollback);
        assertTrue("no rollbackOnly", !tm.rollbackOnly);
    }

    public void testRollbackWithExistingTransaction() {
        TestTransactionManager tm = new TestTransactionManager(true, true);
        TransactionStatus status = tm.getTransaction(null);
        tm.rollback(status);
        assertTrue("no begin", !tm.begin);
        assertTrue("no commit", !tm.commit);
        assertTrue("no rollback", !tm.rollback);
        assertTrue("triggered rollbackOnly", tm.rollbackOnly);
    }

    public void testRollbackOnlyWithExistingTransaction() {
        TestTransactionManager tm = new TestTransactionManager(true, true);
        TransactionStatus status = tm.getTransaction(null);
        status.setRollbackOnly();
        tm.commit(status);
        assertTrue("no begin", !tm.begin);
        assertTrue("no commit", !tm.commit);
        assertTrue("no rollback", !tm.rollback);
        assertTrue("triggered rollbackOnly", tm.rollbackOnly);
    }

    public void testTransactionTemplate() {
        TestTransactionManager tm = new TestTransactionManager(false, true);
        TransactionTemplate template = new TransactionTemplate(tm);
        try {
            template.execute(new TransactionCallbackWithoutResult() {
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                }
            });
            assertTrue("triggered begin", tm.begin);
            assertTrue("triggered commit", tm.commit);
            assertTrue("no rollback", !tm.rollback);
            assertTrue("no rollbackOnly", !tm.rollbackOnly);
        } catch (RuntimeException ex) {
            fail("Should not have thrown RuntimeException");
        }
    }

    public void testTransactionTemplateWithException() {
        TestTransactionManager tm = new TestTransactionManager(false, true);
        TransactionTemplate template = new TransactionTemplate(tm);
        final RuntimeException ex = new RuntimeException("Some application exception");
        try {
            template.execute(new TransactionCallbackWithoutResult() {
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    throw ex;
                }
            });
            fail("Should have propagated RuntimeException");
        } catch (RuntimeException caught) {
            // expected
            assertTrue("Correct exception", caught == ex);
            assertTrue("triggered begin", tm.begin);
            assertTrue("no commit", !tm.commit);
            assertTrue("triggered rollback", tm.rollback);
            assertTrue("no rollbackOnly", !tm.rollbackOnly);
        }
    }

    public void testTransactionTemplateWithRollbackException() {
        final TransactionSystemException tex = new TransactionSystemException("system exception");
        TestTransactionManager tm = new TestTransactionManager(false, true) {
            protected void doRollback(TransactionStatus status) {
                super.doRollback(status);
                throw tex;
            }
        };
        TransactionTemplate template = new TransactionTemplate(tm);
        final RuntimeException ex = new RuntimeException("Some application exception");
        try {
            template.execute(new TransactionCallbackWithoutResult() {
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    throw ex;
                }
            });
            fail("Should have propagated RuntimeException");
        } catch (RuntimeException caught) {
            // expected
            assertTrue("Correct exception", caught == tex);
            assertTrue("triggered begin", tm.begin);
            assertTrue("no commit", !tm.commit);
            assertTrue("triggered rollback", tm.rollback);
            assertTrue("no rollbackOnly", !tm.rollbackOnly);
        }
    }

    public void testTransactionTemplateWithError() {
        TestTransactionManager tm = new TestTransactionManager(false, true);
        TransactionTemplate template = new TransactionTemplate(tm);
        try {
            template.execute(new TransactionCallbackWithoutResult() {
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    throw new Error("Some application error");
                }
            });
            fail("Should have propagated Error");
        } catch (Error err) {
            // expected
            assertTrue("triggered begin", tm.begin);
            assertTrue("no commit", !tm.commit);
            assertTrue("triggered rollback", tm.rollback);
            assertTrue("no rollbackOnly", !tm.rollbackOnly);
        }
    }

    public void testTransactionTemplateInitialization() {
        TestTransactionManager tm = new TestTransactionManager(false, true);
        TransactionTemplate template = new TransactionTemplate();
        template.setTransactionManager(tm);
        assertTrue("correct transaction manager set", template.getTransactionManager() == tm);

        try {
            template.setIsolationLevelName("TIMEOUT_DEFAULT");
            fail("Should have thrown IllegalArgumentException");
        } catch (IllegalArgumentException ex) {
            // expected
        }
        template.setIsolationLevelName("ISOLATION_SERIALIZABLE");
        assertTrue("Correct isolation level set", template.getIsolationLevel() == TransactionDefinition.ISOLATION_SERIALIZABLE);

        try {
            template.setPropagationBehaviorName("TIMEOUT_DEFAULT");
            fail("Should have thrown IllegalArgumentException");
        } catch (IllegalArgumentException ex) {
            // expected
        }
        template.setPropagationBehaviorName("PROPAGATION_SUPPORTS");
        assertTrue("Correct propagation behavior set", template.getPropagationBehavior() == TransactionDefinition.PROPAGATION_SUPPORTS);

    }

}
